<?php


namespace App\Admin\Services\Wechat;


use App\Admin\Exceptions\App\ProjectType\ProjectTypeException;
use App\Admin\Exceptions\Wechat\MiniProgramTemplateException;
use App\Admin\Requests\Wechat\MiniProgramTemplateCreateRequest;
use App\Admin\Requests\Wechat\MiniProgramTemplateUpdateAuditItemListRequest;
use App\Admin\Requests\Wechat\MiniProgramTemplateUpdateDomainRequest;
use App\Admin\Requests\Wechat\MiniProgramTemplateUpdateRequest;
use App\Admin\Services\App\ProjectService;
use App\Repositories\App\AppProjectRepository;
use App\Repositories\App\AppProjectTypeRepository;
use App\Repositories\Wechat\MiniProgramOperationRecordRepository;
use App\Repositories\Wechat\MiniProgramTemplateRepository;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class MiniProgramTemplateService {
    private $_repository;

    public function __construct(MiniProgramTemplateRepository $repository) {
        $this->_repository = $repository;
    }

    /**
     * 代码模板列表
     *
     * @return mixed
     */
    public function page() {
        $templateResult = $this->_repository->queryByConditionAndPage(
            function ($query) {
                $query->with(['appProjectType']);
            }
        );
        if ($templateResult['total_count'] > 0) {
            foreach ($templateResult['list'] as $key => &$val) {
                $appProjectType = $val->appProjectType;
                if ($appProjectType) {
                    $val['app_project_type_name'] = $appProjectType->id . ':' . $appProjectType->name;
                }
                $templateResult['list'][$key]['images_first'] = $val['images'][0] ?? '';
            }
        }
        return $templateResult;
    }

    /**
     * 新建代码模板
     *
     * @param MiniProgramTemplateCreateRequest $request
     * @return \App\Commons\Utils\ModelTools\BaseModel|null
     * @throws MiniProgramTemplateException
     */
    public function store(MiniProgramTemplateCreateRequest $request) {
        $fillData = $request->post();
        $fillAble = $request->getRuleKeys();
        $this->_getProjectTypeAndTemplateOnlineDataById($fillData, $fillAble);
        $miniProgramTemplateEl = $this->_repository->getEloquent()->fillable($fillAble)->fill($fillData);
        $miniProgramTemplateEl->save();
        return $miniProgramTemplateEl;
    }

    /**
     * 删除指定id的代码模板
     *
     * @param $id
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|object|null
     * @throws MiniProgramTemplateException
     */
    public function delete($id) {
        $appProjectEl = (new AppProjectRepository())->getItemByConditions(['mpt_id' => $id]);
        if ($appProjectEl->isNotEmpty()) {
            throw new MiniProgramTemplateException(1100002);
        }
        return $this->_repository->delete($id, null, new MiniProgramTemplateException(110001));
    }

    /**
     * 更新指定id的代码模板
     *
     * @param $id
     * @param MiniProgramTemplateUpdateRequest $request
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|object|null
     * @throws MiniProgramTemplateException
     */
    public function update($id, MiniProgramTemplateUpdateRequest $request) {
        $fillData = $request->post();
        $fillAble = $request->getRuleKeys();
        $this->_getProjectTypeAndTemplateOnlineDataById($fillData, $fillAble, false, true);
        return $this->_repository->update($id, $fillData, $fillAble);
    }

    /**
     * 继承项目类型域名配置和线上微信模板数据
     *
     * @param $fillData
     * @param $fillAble
     * @param bool $updateProjectType
     * @param bool $updateTemplate
     * @throws MiniProgramTemplateException
     */
    private function _getProjectTypeAndTemplateOnlineDataById(&$fillData, &$fillAble, $updateProjectType = true, $updateTemplate = true) {
        //继承代码类型的域名配置
        if ($updateProjectType) {
            $appProjectTypeEl = (new AppProjectTypeRepository())->findItemElById($fillData['app_project_type_id'], new ProjectTypeException(1100001));
            $appProjectData   = [
                'server_domain'             => $appProjectTypeEl->server_domain,
                'webviewdomain'             => $appProjectTypeEl->webviewdomain,
                'wx_submit_audit_item_list' => $appProjectTypeEl->wx_submit_audit_item_list,
            ];
            $fillData         = array_merge($fillData, $appProjectData);
            $fillAble         = array_merge($fillAble, array_keys($appProjectData));
        }

        //继承线上微信列表数据
        if ($updateTemplate) {
            $templateOnline = (new CompanyMiniProgramService())->getTemplateList();
            $templateOnline = array_column($templateOnline, null, 'template_id');
            if (!key_exists($fillData['wx_template_id'], $templateOnline)) {
                throw new MiniProgramTemplateException(1100001);
            }
            $templateData = [
                'wx_created_at'   => date('Y-m-d H:i:s', $templateOnline[$fillData['wx_template_id']]['create_time']),
                'wx_user_version' => $templateOnline[$fillData['wx_template_id']]['user_version'],
                'wx_user_desc'    => $templateOnline[$fillData['wx_template_id']]['user_desc'],
                'wx_developer'    => $templateOnline[$fillData['wx_template_id']]['developer'],
            ];
            $fillData     = array_merge($fillData, $templateData);
            $fillAble     = array_merge($fillAble, array_keys($templateData));
        }
    }

    /**
     * 根据项目类型id获取模板列表
     *
     * @param $appProjectTypeId
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|object|null
     * @throws ProjectTypeException
     */
    public function getTemplateListByAppProjectId($appProjectTypeId) {
        $appProjectTypeEl = (new AppProjectTypeRepository())->getItemByConditions(
            ['id' => $appProjectTypeId],
            null,
            true
        );
        if ($appProjectTypeEl) {
            return $this->_repository->getItemByConditions(
                ['app_project_type_id' => $appProjectTypeId]
            );
        } else {
            throw new ProjectTypeException(1100001);
        }
    }

    /**
     * 修改代码模板的服务器域名及业务域名
     *
     * @param $id
     * @param MiniProgramTemplateUpdateDomainRequest $request
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * @throws \Exception
     */
    public function updateDomain($id, MiniProgramTemplateUpdateDomainRequest $request) {
        return $this->_repository->update($id, $request->post(), $request->getRuleKeys());
    }

    /**
     * 根据指定模板批量更新小程序
     *
     * @param $id
     *
     * @return null
     */
    public function batchPublishMiniProgramByTemplateId($id) {
        $miniProgramTemplateEl = $this->_repository->findItemElById($id, new ProjectTypeException(1100001));

        $appProjectCol = AppProjectRepository::getInstance()->getProjectsByMiniProgramTemplateId($id, function ($query) {
            $query->with(['appProjectType', 'miniProgramTemplate']);
        });

        $batchOpName  = "根据指定模板批量更新小程序-{$miniProgramTemplateEl->name}";
        $opUserId     = Auth::user()->id;
        $batchCount   = $appProjectCol->count();
        $opCommonInfo = [
            'user_id'   => $opUserId,
            'op_result' => \App\Models\Wechat\MiniProgramOperationRecord::OP_RESULT_NONE,
            'mpt_id'    => $miniProgramTemplateEl->id,
        ];

        //限制频繁操作,设置个限制操作的缓存
        $this->batchOpByTemplateIdNextOperationBufferCache($id, $batchCount, self::BATCH_PUBLISH_MINI_PROGRAM_NEXT_OPERATION_BUFFER_CACHE_NAME_PRE);

        //批量处理开始记录
        MiniProgramOperationRecordRepository::getInstance()->store($opCommonInfo + [
                'op_name' => "{$batchOpName}--开始",
                'result' => json_encode([
                    'project_count'  => $batchCount,
                    'id'             => $id,
                    'wx_template_id' => $miniProgramTemplateEl->wx_template_id,
                ]),
            ]);

        $successCount = 0;

        foreach ($appProjectCol as $appProjectEl) {
            try {
                ProjectService::publishMiniProgramByProjectEl($appProjectEl);
                $successCount++;
            } catch (\Exception $e) {
                Log::error('指定模板更新项目代码异常：' . $e->getMessage());
                Log::error($e->getTraceAsString());
            }
        }


        //批量处理结果记录
        MiniProgramOperationRecordRepository::getInstance()->store($opCommonInfo + [
                'op_name' => "{$batchOpName}--结束",
                'result'  => json_encode([
                    'project_count'  => $batchCount,
                    'success_count'  => $successCount,
                    'error_count'    => $batchCount - $successCount,
                    'id'             => $id,
                    'wx_template_id' => $miniProgramTemplateEl->wx_template_id,
                ]),
            ]);

        return null;
    }
    
    
    const BATCH_PUBLISH_MINI_PROGRAM_NEXT_OPERATION_BUFFER_CACHE_NAME_PRE             = 'batchPublishMiniProgramByTemplate_';
    const BATCH_REVERT_MINI_PROGRAM_CODE_RELEASE_NEXT_OPERATION_BUFFER_CACHE_NAME_PRE = 'batchRevertMiniProgramCodeReleaseByTemplate_';

    /**
     * 限制频繁执行批量操作,设置个限制操作的缓存
     *
     * @param     $id
     * @param     $batchCount
     * @param int $averageProcessingSecond 每个项目预计处理时间（秒)
     */
    public function batchOpByTemplateIdNextOperationBufferCache($id, $batchCount, $cachePre, $averageProcessingSecond = 5) {
        $nextOperationBufferCacheName   = $cachePre . $id;
        $nextOperationBufferCacheSecond = $averageProcessingSecond * $batchCount;
        Cache::put($nextOperationBufferCacheName, time() + $nextOperationBufferCacheSecond, $nextOperationBufferCacheSecond / 60);
    }

    /**
     * 检测是否频繁执行操作
     *
     * @param $id
     *
     * @return bool
     * @throws ProjectTypeException
     */
    public function batchPublishMiniProgramByTemplateIdNextOperationBufferCheck($id, $cachePre) {
        $nextOperationBufferCacheName = $cachePre . $id;

        $nextTimestamp = Cache::get($nextOperationBufferCacheName);

        if ($nextTimestamp) {
            throw new ProjectTypeException(1100004);
        }

        return true;
    }

    /**
     * 获取搜索过滤魔板列表
     * @return array
     */
    public function getTemplateListForSearchFilter() {
        $miniProgramTemplateCol = $this->_repository->getItemByConditions(
            [],
            function ($query) {
                $query->select(['id', 'name', 'app_project_type_id'])
                    ->with(['appProjectType' => function ($query) {
                        $query->select(['id', 'name']);
                    }])
                    ->orderBy('id', 'desc');
            }
        );

        $miniProgramTemplateGroupCol = $miniProgramTemplateCol->groupBy('app_project_type_id');

        $resultList = [];

        foreach ($miniProgramTemplateGroupCol as $typeId => $miniProgramTemplateCol) {
            $resultList[] = [
                'value'    => "type_{$typeId}",
                'label'    => '☒' . ($miniProgramTemplateCol[0]->appProjectType->name ?? '未定义'),
                'disabled' => true
            ];

            foreach ($miniProgramTemplateCol as $miniProgramTemplateEl) {
                $resultList[] = [
                    'value' => $miniProgramTemplateEl->id,
                    'label' => '﹌﹌' . $miniProgramTemplateEl->name,
                ];
            }
        }

        return $resultList;
    }

    /**
     * 检查模板可用性
     *
     * @return array
     */
    public function check() {
        $templateLocal       = $this->_repository->getItemByConditions(
            [],
            function ($query) {
                $query->select(['id', 'name', 'wx_template_id']);
            }
        );
        $nonExistentTemplate = [];
        if ($templateLocal->isNotEmpty()) {
            $templateLocal = array_column($templateLocal->toArray(), null, 'wx_template_id');
            //本地对应线上微信模板的id
            $templateLocalId = array_map(function ($val) {
                return $val['wx_template_id'];
            }, $templateLocal);

            //线上微信模板的id
            $templateOnline   = (new CompanyMiniProgramService())->getTemplateList();
            $templateOnlineId = array_map(function ($val) {
                return $val['template_id'];
            }, $templateOnline);

            //线上微信模板不存在的数据
            foreach ($templateLocalId as $localId) {
                if (!in_array($localId, $templateOnlineId)) {
                    array_push($nonExistentTemplate, $templateLocal[$localId]);
                }
            }
        }
        return $nonExistentTemplate;
    }

    /**
     * 修改审核项列表
     *
     * @param $id
     * @param MiniProgramTemplateUpdateAuditItemListRequest $request
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * @throws \Exception
     */
    public function updateAuditItemList($id, MiniProgramTemplateUpdateAuditItemListRequest $request) {
        return $this->_repository->update($id, $request->post(), $request->getRuleKeys(), new MiniProgramTemplateException(1100001));
    }
    
    /**
     * 回退该模板下小程序项目版本
     *
     * @param $id
     *
     * @return null
     * @throws \Exception
     */
    public function batchRevertMiniProgramCodeReleaseByTemplateId($id) {
        $miniProgramTemplateEl = $this->_repository->findItemElById($id, new ProjectTypeException(1100001));
        
        $appProjectCol = AppProjectRepository::getInstance()->getProjectsByMiniProgramTemplateId($id, function ($query) {
            $query->with(['appProjectType', 'miniProgramTemplate']);
        });
        
        $batchOpName  = "根据指定模板批量回退小程序版本-{$miniProgramTemplateEl->name}";
        $opUserId     = Auth::user()->id;
        $batchCount   = $appProjectCol->count();
        $opCommonInfo = [
            'user_id'   => $opUserId,
            'op_result' => \App\Models\Wechat\MiniProgramOperationRecord::OP_RESULT_NONE,
            'mpt_id'    => $miniProgramTemplateEl->id,
        ];
        
        //限制频繁操作,设置个限制操作的缓存
        $this->batchOpByTemplateIdNextOperationBufferCache($id, $batchCount, self::BATCH_REVERT_MINI_PROGRAM_CODE_RELEASE_NEXT_OPERATION_BUFFER_CACHE_NAME_PRE);
        
        //批量处理开始记录
        MiniProgramOperationRecordRepository::getInstance()->store($opCommonInfo + [
                'op_name' => "{$batchOpName}--开始",
                'result'  => json_encode([
                    'project_count'  => $batchCount,
                    'id'             => $id,
                    'wx_template_id' => $miniProgramTemplateEl->wx_template_id,
                ]),
            ]);
        
        $successCount = 0;
        foreach ($appProjectCol as $appProjectEl) {
            try {
                ProjectService::getMiniProgramToolByProjectEl($appProjectEl)->rollbackRelease();
                $successCount++;
            } catch (\Exception $e) {
                Log::error('指定模板更新项目代码异常：' . $e->getMessage());
                Log::error($e->getTraceAsString());
            }
        }
        
        //批量处理结果记录
        MiniProgramOperationRecordRepository::getInstance()->store($opCommonInfo + [
                'op_name' => "{$batchOpName}--结束",
                'result'  => json_encode([
                    'project_count'  => $batchCount,
                    'success_count'  => $successCount,
                    'error_count'    => $batchCount - $successCount,
                    'id'             => $id,
                    'wx_template_id' => $miniProgramTemplateEl->wx_template_id,
                ]),
            ]);
        
        return null;
    }
}